#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project ：量化交易探究 
@File ：贪婪恐惧指数爬虫.py
@IDE  ：PyCharm 
@Author ：李涵彬
@Date ：2025/3/27 下午8:03 
"""

import requests
import pandas as pd
from datetime import datetime
from typing import Optional
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from fake_useragent import UserAgent  # 新增随机UA生成

# ================ Server酱配置 ================ [1,3,8](@ref)
SERVER_CHAN_SCKEY = "SCT223730TpAaEkEqs3V3RvoavFlUF5MKY"  # 替换为从https://sct.ftqq.com获取的密钥
SERVER_CHAN_API = f"https://sctapi.ftqq.com/{SERVER_CHAN_SCKEY}.send"

# ================ 请求重试配置 ================ [3,8](@ref)
retry_strategy = Retry(
	total=3,
	backoff_factor=1,
	status_forcelist=[429, 500, 502, 503, 504],
	allowed_methods=["GET"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)


def send_wechat_alert(score: int, sentiment: str) -> bool:
	"""通过Server酱发送微信通知[1,3,8](@ref)"""
	try:
		title = f"⚠️ 加密情绪警报({score}-{sentiment})"
		desp = f"""
### 当前指数值：{score} ({sentiment})
**触发条件**：{"极端恐惧" if score <= 30 else "极端贪婪"}
**更新时间**：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

**历史参考**：
- 0-20：极端恐惧（抄底机会）
- 21-44：恐惧（观察信号）
- 45-55：中性（保持仓位）
- 56-74：贪婪（警惕回调）
- 75-100：极端贪婪（止盈信号）[5](@ref)
        """
		response = requests.post(
			SERVER_CHAN_API,
			data={"title": title, "desp": desp},
			timeout=10
		)
		return response.status_code == 200
	except Exception as e:
		print(f"微信通知发送失败：{str(e)}")
		return False


def fetch_fear_greed_index(limit: int = 30, save_csv: bool = True) -> Optional[pd.DataFrame]:
	"""
	爬取加密货币恐惧贪婪指数[3,8](@ref)
	:param limit: 获取历史数据的天数（默认30天）
	:param save_csv: 是否保存为CSV文件
	:return: DataFrame格式数据
	"""
	# ================ 请求配置优化 ================ [3,8](@ref)
	session = requests.Session()
	session.mount("https://", adapter)

	headers = {
		"User-Agent": UserAgent().random,  # 随机UA
		"Accept-Language": "en-US,en;q=0.9",
		"Referer": "https://alternative.me/"
	}

	try:
		response = session.get(
			"https://api.alternative.me/fng/",
			params={
				'limit': limit,
				'format': 'json',
				'date_format': 'world'
			},
			headers=headers,
			timeout=15
		)

		if response.status_code == 200:
			# ================ 数据处理优化 ================ [3,8](@ref)
			df = pd.DataFrame(response.json()['data'])

			# 类型转换与清洗
			df = df.astype({
				'value': 'int16',
				'value_classification': 'category'
			})

			# 日期处理增强
			df['date'] = pd.to_datetime(
				df['timestamp'],
				format="%d-%m-%Y",
				errors='coerce'
			)
			df = df.dropna(subset=['date'])

			# 重命名与排序
			df = df.rename(columns={
				'value': 'score',
				'value_classification': 'sentiment'
			})[['date', 'score', 'sentiment']]

			df = df.sort_values('date', ascending=False).reset_index(drop=True)

			# ================ 警报触发逻辑 ================ [1,3](@ref)
			latest_score = df.iloc[0]['score']
			latest_sentiment = df.iloc[0]['sentiment']
			print(f"当前贪婪恐惧指数为：{latest_score}，处于{latest_sentiment}情绪")

			if latest_score <= 30 or latest_score >= 80:
				alert_sent = send_wechat_alert(
					latest_score,
					df.iloc[0]['sentiment']
				)
				print(f"警报通知{'成功' if alert_sent else '失败'}")

			# ================ 数据保存 ================
			if save_csv:
				filename = f"crypto_fear_greed_{datetime.now().strftime('%Y%m%d')}.csv"
				df.to_csv(filename, index=False)
				print(f"数据已保存至 {filename}")

			return df

		else:
			print(f"请求失败，状态码：{response.status_code}")
			return None

	except requests.exceptions.RequestException as e:
		print(f"网络请求异常：{type(e).__name__}")
	except KeyError as e:
		print(f"JSON解析异常，缺失关键字段：{e}")
	except Exception as e:
		print(f"未知异常：{str(e)}")

	return None


if __name__ == '__main__':
	# 示例用法：获取最近60天数据
	df = fetch_fear_greed_index(limit=60)

	if df is not None:
		print("\n最新5条数据：")
		print(df.head())

	# 调试用极端值测试
	# df.iloc[0, df.columns.get_loc('score')] = 25  # 测试恐惧警报
	# df.iloc[0, df.columns.get_loc('score')] = 85  # 测试贪婪警报